#ifndef IVIDEORECEIVER_H
#define IVIDEORECEIVER_H

#include <QObject>
#include <QDebug>
#include <QAbstractVideoSurface>
#include <QVideoSurfaceFormat>


class IVideoReceiver : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface)

public:
    IVideoReceiver(QObject *parent = nullptr) : QObject(parent) {}

    virtual ~IVideoReceiver() {};

    virtual QAbstractVideoSurface* videoSurface() const { return _videoSurface; };

    virtual bool decoding() = 0;

    enum VideoStatus {
        SV_START_PLAY     = 0,
        SV_START_RECORD   = 1,
        SV_START_PUSH     = 2,
        SV_STOP_PLAY      = 3,
        SV_STOP_RECORD    = 4,
        SV_STOP_PUSH      = 5,

        SV_PLAYING        = 6,
        SV_RECORDING      = 7,
        SV_PUSHING        = 8,
        SV_OPENVIDEO      = 9,
        SV_STOPPED        = 10,
        SV_FAILED         = 99,
    };
    Q_ENUM(VideoStatus)

    typedef enum {
        FILE_FORMAT_MIN = 0,
        FILE_FORMAT_MKV = FILE_FORMAT_MIN,
        FILE_FORMAT_MOV,
        FILE_FORMAT_MP4,
        FILE_FORMAT_MAX
    } FILE_FORMAT;
    Q_ENUM(FILE_FORMAT)

    typedef enum {
        STATUS_OK = 0,
        STATUS_FAIL,
        STATUS_INVALID_STATE,
        STATUS_INVALID_URL,
        STATUS_NOT_IMPLEMENTED,
        STATUS_PATH_ERROR
    } STATUS;
    Q_ENUM(STATUS)

signals:
    void sigVideoPlayFailed ();
    void videoTimeout       (void);
    void streamingChanged   (bool active);
    void decodingChanged    (bool active);
    void recordingChanged   (bool active);
    void recordingStarted   (void);
    void videoSizeChanged   (QSize size);
    void pushingChanged     (bool active);
    void sigVideoStatus     (int sstatus);

    void onStartComplete           (STATUS status);
    void onStopComplete            (STATUS status);
    void onStartDecodingComplete   (STATUS status);
    void onStopDecodingComplete    (STATUS status);
    void onStartRecordingComplete  (STATUS status);
    void onStopRecordingComplete   (STATUS status);
    void onTakeScreenshotComplete  (STATUS status);

    void onStartPushingComplete (STATUS status);
    void onStopPushingComplete  (STATUS status);

public slots:
    virtual void init(void) {}

    virtual void start(const QString& uri, unsigned timeout, int buffer = 0, int cacheTime = -1) = 0;

    virtual void stop(void) = 0;

    virtual void restart(unsigned long waitTime = 50 /*ms*/) = 0;

    virtual void startDecoding(void *sink) = 0;

    virtual void stopDecoding(void) = 0;

    virtual void setVideoSurface(QAbstractVideoSurface* videoSurface)
    {
        if (_videoSurface && videoSurface != _videoSurface && _videoSurface->isActive()) {
            _videoSurface->stop();
        }
        _videoSurface = videoSurface;
        if (_videoSurface && _format.isValid()) {
            _format = _videoSurface->nearestFormat(_format);
            _videoSurface->start(_format);
        }
    }

    virtual void setFormat(unsigned int width, unsigned int height, unsigned int format)
    {
        QSize size(width, height);
        QVideoSurfaceFormat vformat(size, static_cast<QVideoFrame::PixelFormat>(format));
        _format = vformat;
        if (_videoSurface) {
            if (_videoSurface->isActive()) {
                _videoSurface->stop();
            }
            _format = _videoSurface->nearestFormat(_format);
            _videoSurface->start(_format);
        }
    }

    virtual void onNewVideoContent(const QVideoFrame& frame)
    {
        if (!_imgSavePath.isEmpty()) {
            if (frame.isValid()) {
                QVideoFrame dupFrame(frame);
                dupFrame.map(QAbstractVideoBuffer::ReadOnly);
                QImage::Format format = QVideoFrame::imageFormatFromPixelFormat(dupFrame.pixelFormat());
                if (format != QImage::Format_Invalid) {
                    QImage img(dupFrame.bits(), dupFrame.width(), dupFrame.height(), format);
                    bool res = img.save(_imgSavePath);
                    qDebug() << QString("Image Path:%1 Save %2").arg(_imgSavePath).arg(res ? "Success" : "Failed");
                }
                dupFrame.unmap();
            } else {
                qWarning() << "Image Save Data is Invalid!";
            }
            _imgSavePath.clear();
        }
        if (_videoSurface) {
            _videoSurface->present(frame);
        }
    }

    void setDecoding(bool val)
    {
        if (_decoding != val) {
            _decoding = val;
            emit decodingChanged(_decoding);
        }
    }

    static QString statusStr(VideoStatus status)
    {
        if(status == SV_START_PLAY) {
            return QString("Start-Play");
        } else if(status == SV_START_RECORD) {
            return QString("Start-Record");
        } else if(status == SV_START_PUSH) {
            return QString("Start-Push");
        } else if(status == SV_STOP_PLAY) {
            return QString("Stop-Play");
        } else if(status == SV_STOP_RECORD) {
            return QString("Stop-Record");
        } else if(status == SV_STOP_PUSH) {
            return QString("Stop-Push");
        } else if(status == SV_PLAYING) {
            return QString("Video-Playing");
        } else if(status == SV_RECORDING) {
            return QString("Video-Recording");
        } else if(status == SV_PUSHING) {
            return QString("Video-Pushing");
        } else if(status == SV_OPENVIDEO) {
            return QString("Open-Video");
        } else if(status == SV_STOPPED) {
            return QString("Video-Stoped");
        } else if(status == SV_FAILED) {
            return QString("Video-Failed");
        }
        return QString("");
    }

protected:
    QAbstractVideoSurface* _videoSurface = nullptr;
    QVideoSurfaceFormat    _format;
    QString                _imgSavePath;
    bool                   _decoding = false;
};

#endif // IVIDEORECEIVER_H
